package com.gitee.zhanyao.common.util;

import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.text.NumberFormat;

/**
 * @author zhanyao
 */
@Slf4j
public class MathUtil {

    private MathUtil() {
    }

    /**
     * 根据有效位返回四舍六入五成双结果
     *
     * @param number           double 数据
     * @param significantDigit 有效位
     * @return 格式化的字符串
     */
    public static String setMaximumFractionDigitsBySignificantDigit(double number, int significantDigit) {
        if (significantDigit < 1) {
            log.warn("significantDigit 为 {},有效位数小于1", significantDigit);
            return String.valueOf(number);
        }
        // 转换为 x.xxx * 10^n
        // 求 n
        double n = Math.log10(number);
        double tenPower = 0;
        if (n >= 0) {
            tenPower = Math.floor(n);
        } else {
            tenPower = Math.ceil(n);
        }
        // 求 x.xxx
        double tmpNumber = number / Math.pow(10, tenPower);
        int decimalDigits = MathUtil.getDecimalDigits(tmpNumber, significantDigit);
        String tmpNumberStr = setMaximumFractionDigits(tmpNumber, decimalDigits);
        // 转换为 xxxx.xxxx
        tmpNumber = Double.parseDouble(tmpNumberStr) * Math.pow(10, tenPower);
        return BigDecimal.valueOf(tmpNumber).stripTrailingZeros().toPlainString();
    }

    /**
     * 根据有效位数返回小数位数
     *
     * @param number           double 数据，取值范围 (-10, 10)
     * @param significantDigit 有效位
     * @return 小数位数
     */
    public static int getDecimalDigits(double number, int significantDigit) {
        double numberAbs = Math.abs(number);
        // [0, 1)，有效位就是小数要保留的位数
        if (numberAbs < 1) {
            return significantDigit;
        }
        // [1, 10)，小数要保留位数为有效位-1
        String numberStr = String.valueOf(numberAbs);
        int dotIndex = numberStr.indexOf(".");
        if (dotIndex < 0 || significantDigit < dotIndex) {
            return 0;
        } else {
            return significantDigit - dotIndex;
        }
    }

    /**
     * 四舍六入五成双
     * 是一种比较精确比较科学的计数保留法，是一种数字修约规则。
     *
     * @param number   double 数据
     * @param decimalDigits 要显示的最小数字位数；如果小于零，则使用零。具体的子类可能会强制适用于正在格式化的数字类型的此值的上限
     * @return 格式化的字符串
     */
    public static String setMaximumFractionDigits(double number, int decimalDigits) {
        NumberFormat nf = NumberFormat.getInstance();
        nf.setMaximumFractionDigits(decimalDigits);
        return nf.format(number);
    }

}
